/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "entity.h"
#include "graphics.h"
#include "events.h"

#include <iostream>

namespace tri {

unsigned int TEntity::s_EntityUIDCount = 0;

TAbstractEntityParent::TAbstractEntityParent(){
	
}

TAbstractEntityParent::~TAbstractEntityParent(){}

TEntity::TEntity() : TTransform(), TAbstractEntityParent(), TAbstractEventComponent(),m_Parent(NULL),m_LoadUID(true), m_isVisible(true){
	m_EntityUID = ++s_EntityUIDCount;
}

TEntity::~TEntity() {
}

unsigned int TEntity::getUID() const{
	return m_EntityUID;
}

void TEntity::setUIDLoad(bool b){
	m_LoadUID = b;
}

void TEntity::update(){
	emit(tri::UPDATED);
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();
	while (it != itEnd) {
		(*it)->update();
		++it;
	}
}

void TEntity::render(){
	
	if (!m_isVisible){
		return;
	}
	
	if (m_LoadUID){
		glLoadName(getUID());
	}
	
	//set render settings
	if(isWireFrameMode()){
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	}
	getMaterialPointer()->apply();
	
	glPushMatrix();
	applyTransform();
	tri::TVector _scale = getScale();
	glScaled(_scale.x,_scale.y,_scale.z);
	display();
	
	
	//undo render settings
	glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
	
	
	renderChildren();
	
	glPopMatrix();
}

void TEntity::renderChildren(){
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();
	while (it != itEnd) {
		(*it)->render();
		++it;
	}
}

void TEntity::setColor(int r, int g, int b, int a) {
	mMaterial.setColor(r,g,b,a);
}

void TEntity::setTargetColor(int r, int g, int b, int a, float t) {
	mMaterial.setTargetColor(tri::graphics::RGBA(r, g, b, a), t);
}

tri::graphics::TColor TEntity::getColor(){
	return mMaterial.getColor();
}

graphics::RGBA TEntity::getColorAsRGBA(){
	return mMaterial.getColorAsRGBA();
}

void TEntity::applyColor(){
	mMaterial.getColor().apply();
}

void TEntity::setColor(graphics::RGBA c){
	mMaterial.setColor(c);
}

void TEntity::setTargetColor(graphics::RGBA c,float t){
	mMaterial.setTargetColor(c,t);
}

void TEntity::setImage(std::string image,bool filter,bool mipmap) {
	mMaterial.setImage(image,filter,mipmap);
}

std::string TEntity::getImage() {
	return mMaterial.getImage();
}

bool TEntity::isImageFiltered(){
	return mMaterial.isImageFiltered();
}

bool TEntity::isImageMipmap(){
	return mMaterial.isImageMipmap();
}

tri::TTexture* TEntity::getTexture(){
	return mMaterial.getTexture();
}

void TEntity::setMaterial(std::string mat){
	mMaterialPointer = mat;
}

std::string TEntity::getMaterial(){
	return mMaterialPointer;
}

TMaterial* TEntity::getMaterialPointer(){
	TMaterial* mat = TMaterial::getMaterial(mMaterialPointer);
	if (mat!=NULL){
		return mat;
	}
	return &mMaterial;
}

void TEntity::removeEntity(TEntity* child){
	if( child != NULL ){
		m_EntitySet.erase(child);
		child->m_Parent = NULL;
	}
}

void TEntity::addEntity(TEntity* child){
	if ( child == NULL ){
		return;
	}
	
	if( !hasEntity(child) ){
		if (child->m_Parent != NULL){
			child->m_Parent->removeEntity(child);
		}
		
		m_EntitySet.insert(child);
		child->m_Parent = this;
	}
}


void TEntity::removeAll(){
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();

	while (it != itEnd) {
		(*it)->m_Parent = NULL; 
		++it;
	}
	
	m_EntitySet.erase(m_EntitySet.begin(), m_EntitySet.end());
}

bool TEntity::hasEntity(TEntity* child){
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();

	while (it != itEnd) {
		if ( (*it) == child ){
			return true;
		}
		++it;
	}
	
	return false;
}

TAbstractEntityParent* TEntity::getParent(){
	return m_Parent;
}

void TEntity::setParent(TAbstractEntityParent* parent){
	if(m_Parent == NULL){
		m_Parent = parent;
	}
}

void TEntity::getEntitySet(std::set<TEntity*>& r_EntSet){
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();
	r_EntSet.clear();
	while (it != itEnd) {
		r_EntSet.insert(*it);
		++it;
	}
}

TEntity* TEntity::getEntity(unsigned int uid){
	ConstIterator it = m_EntitySet.begin();
	ConstIterator itEnd = m_EntitySet.end();
	
	if( getUID() == uid ){
		return this;
	}
	
	while (it != itEnd) {
		TEntity* entity = (*it)->getEntity(uid);
		if( entity!=NULL ){
			return entity;
		}
		++it;
	}
	
	return NULL;
}

void TEntity::setVisible(bool b){
	m_isVisible = b;
}

bool TEntity::isVisible(){
	return m_isVisible;
}

}
